import 'dart:io';
import 'dart:math';
import 'dart:typed_data';
import 'package:crypto/crypto.dart';
import 'package:flutter/foundation.dart';
import 'dart:convert';

/// 文件哈希算法类型
enum HashAlgorithm {
  /// SHA-256 哈希算法
  sha256,

  /// 快速哈希算法 (小文件<5MB使用全量SHA-256，中小文件使用首尾CRC32，大文件使用抽样哈希)
  fast,
}

/// 文件哈希工具类
/// 提供多种哈希算法和针对不同文件大小的优化策略
class FileHashUtils {
  // 文件大小阈值
  static const int _smallFileSizeThreshold = 5 * 1024 * 1024; // 5MB
  static const int _mediumFileSizeThreshold = 100 * 1024 * 1024; // 100MB

  // 抽样块大小
  static const int _sampleBlockSize = 2 * 1024 * 1024; // 2MB

  // 中间抽样块数量
  static const int _middleSampleCount = 3;

  /// 计算文件哈希值
  /// [file] 要计算的文件
  /// [algorithm] 哈希算法类型
  /// 返回哈希值字符串
  static Future<String> computeFileHash(
    File file, {
    HashAlgorithm algorithm = HashAlgorithm.fast,
  }) async {
    try {
      if (!file.existsSync()) {
        throw Exception('文件不存在: ${file.path}');
      }

      final fileSize = file.lengthSync();

      // 根据哈希算法类型计算哈希值
      switch (algorithm) {
        case HashAlgorithm.sha256:
          return await _computeSha256(file);
        case HashAlgorithm.fast:
          // 根据文件大小选择不同的哈希策略
          if (fileSize <= _smallFileSizeThreshold) {
            // 小文件：直接全量计算SHA-256
            debugPrint('使用全量SHA-256计算小文件哈希: ${file.path}');
            return await _computeSha256(file);
          } else if (fileSize <= _mediumFileSizeThreshold) {
            // 中小文件：使用首尾CRC32+文件信息组合哈希
            debugPrint('使用首尾CRC32计算中小文件哈希: ${file.path}');
            return await _computeMediumFileHash(file, fileSize);
          } else {
            // 大文件：使用抽样哈希
            debugPrint('使用抽样哈希计算大文件哈希: ${file.path}');
            return await _computeLargeFileHash(file, fileSize);
          }
      }
    } catch (e) {
      debugPrint('计算文件哈希出错: $e');
      rethrow;
    }
  }

  /// 计算完整SHA-256哈希值
  static Future<String> _computeSha256(File file) async {
    try {
      final bytes = await file.readAsBytes();
      final digest = sha256.convert(bytes);
      return digest.toString();
    } catch (e) {
      if (e is OutOfMemoryError) {
        // 文件太大导致内存不足，使用流式计算
        debugPrint('文件过大，使用流式计算SHA-256');
        return await _computeSha256WithStream(file);
      }
      rethrow;
    }
  }

  /// 使用流式计算SHA-256哈希值，避免内存不足问题
  static Future<String> _computeSha256WithStream(File file) async {
    try {
      // 创建一个输入流
      final input = file.openRead();

      // 创建一个累积器
      var digest = sha256.convert(<int>[]);
      var hash = digest.toString();

      // 分块读取文件并更新哈希
      await for (final chunk in input) {
        digest = sha256.convert([...digest.bytes, ...chunk]);
        hash = digest.toString();
      }

      return hash;
    } catch (e) {
      debugPrint('流式计算SHA-256出错: $e');
      rethrow;
    }
  }

  /// 计算中小文件哈希值 (首尾CRC32+文件信息)
  static Future<String> _computeMediumFileHash(File file, int fileSize) async {
    final fileExtension = file.path.split('.').last.toLowerCase();

    // 读取文件头部
    final headBytes = await _readFileRange(
      file,
      0,
      min(_sampleBlockSize, fileSize),
    );

    // 读取文件尾部
    int tailStart = max(0, fileSize - _sampleBlockSize);
    final tailBytes = await _readFileRange(file, tailStart, fileSize);

    // 计算头部CRC32 (使用自定义实现，因为crc32_checksum可能有兼容性问题)
    final headCrc = _calculateCrc32(headBytes);

    // 计算尾部CRC32
    final tailCrc = _calculateCrc32(tailBytes);

    // 组合哈希：文件大小_头部CRC32_尾部CRC32_文件扩展名
    final combinedHash = '${fileSize}_$headCrc\_$tailCrc\_$fileExtension';

    // 返回SHA-256哈希值以保持统一格式
    return sha256.convert(utf8.encode(combinedHash)).toString();
  }

  /// 计算大文件哈希值 (确定性抽样)
  static Future<String> _computeLargeFileHash(File file, int fileSize) async {
    // 1. 先读取小部分内容计算种子
    final seedBytes = await _readFileRange(file, 0, min(1024, fileSize));
    final seedDigest = sha256.convert(seedBytes);

    // 2. 使用哈希前4字节作为种子
    final seedHex = seedDigest.toString().substring(0, 8);
    final seed = int.parse(seedHex, radix: 16);

    // 3. 确定抽样位置
    final samplePositions = _determineSamplePositions(seed, fileSize);

    // 4. 读取抽样数据并计算哈希
    final combinedData = <int>[];

    // 添加文件大小信息
    combinedData.addAll(utf8.encode(fileSize.toString()));

    // 读取并添加各个抽样点的数据
    for (var position in samplePositions) {
      int start = position;
      int end = min(position + _sampleBlockSize, fileSize);

      if (start >= fileSize) continue;

      final blockBytes = await _readFileRange(file, start, end);
      final blockCrc = _calculateCrc32(blockBytes);
      combinedData.addAll(utf8.encode(blockCrc));
    }

    // 添加文件扩展名
    final fileExtension = file.path.split('.').last.toLowerCase();
    combinedData.addAll(utf8.encode(fileExtension));

    // 计算最终哈希值
    return sha256.convert(combinedData).toString();
  }

  /// 读取文件指定范围的字节
  static Future<Uint8List> _readFileRange(File file, int start, int end) async {
    final raf = await file.open(mode: FileMode.read);
    try {
      await raf.setPosition(start);
      return await raf.read(end - start);
    } finally {
      await raf.close();
    }
  }

  /// 根据种子确定抽样位置
  static List<int> _determineSamplePositions(int seed, int fileSize) {
    final rand = Random(seed);
    final positions = <int>[];

    // 添加头部
    positions.add(0);

    // 添加尾部
    positions.add(max(0, fileSize - _sampleBlockSize));

    // 添加中间抽样点
    final availableRange = fileSize - 2 * _sampleBlockSize;
    if (availableRange > 0) {
      for (int i = 0; i < _middleSampleCount; i++) {
        // 确保抽样点分布均匀
        final section = availableRange / (_middleSampleCount + 1);
        final baseOffset = section * (i + 1);
        // 在每个区段内随机偏移
        final randomOffset = rand.nextInt(section.toInt());

        final position = _sampleBlockSize + baseOffset.toInt() + randomOffset;
        positions.add(min(position, fileSize - _sampleBlockSize));
      }
    }

    // 确保位置唯一且排序
    return positions.toSet().toList()..sort();
  }

  /// 计算数据的CRC32校验和 (简单实现)
  static String _calculateCrc32(Uint8List data) {
    // 使用内置的Hash类计算
    final hash = _crc32(data);
    return hash.toRadixString(16).padLeft(8, '0');
  }

  /// 简单的CRC32实现
  static int _crc32(Uint8List bytes) {
    final crcTable = _makeCrcTable();
    int crc = 0xFFFFFFFF;

    for (var byte in bytes) {
      crc = crcTable[(crc ^ byte) & 0xFF] ^ ((crc >> 8) & 0xFFFFFFFF);
    }

    return (crc ^ 0xFFFFFFFF) & 0xFFFFFFFF;
  }

  /// 创建CRC32表
  static List<int> _makeCrcTable() {
    final crcTable = List<int>.filled(256, 0);

    for (int i = 0; i < 256; i++) {
      int c = i;
      for (int j = 0; j < 8; j++) {
        if (c & 1 == 1) {
          c = 0xEDB88320 ^ ((c >> 1) & 0x7FFFFFFF);
        } else {
          c = (c >> 1) & 0x7FFFFFFF;
        }
      }
      crcTable[i] = c;
    }

    return crcTable;
  }
}
